import {
  ColorSchema,
  CSPoint,
  IData,
  IFieldData,
  IShapeData,
  isShapeData,
  isStreamlineData,
  IStreamlineData,
  POINT_TYPE,
  RENDERORDER,
} from '@amcax/base';
import { GirdType } from '@amcax/renderer';
import { ctx2, dataManager } from '@app-base/module';
import { Picker, Renderer } from '@app-nextmesh/common/utils';
import { documentManager } from '@app-nextmesh/manager';
import { GlobalLineMaterial } from '@app-nextmesh/utils/model/materials';
import { isArray } from 'lodash';
import {
  BufferAttribute,
  Color,
  DoubleSide,
  LineBasicMaterial,
  MeshPhongMaterial,
  Plane,
  Vector3Tuple,
} from 'three-legacy';
import { IMeshData } from '../types';
import { refreshCanvas } from './canvas';
import { makeColorMap } from './colormap';
import {
  makeFieldDxid,
  makeMesh,
  makeStreamlineMesh,
  makeTexturePoints,
  makeWireframeDxid,
  makeWireframeLine,
} from './model';
import { findModelInGroup } from './modelUtils';
import { createModelInfo } from './parseModel';
import { makeWires } from './wireframeUtils';

const PointType = {
  0: POINT_TYPE.VERTEX,
  1: POINT_TYPE.DEFAULT,
  2: POINT_TYPE.ONLINE,
};

/**
 * 清除场景中的模型，包括网格、网格线框、流线等
 */
const clearModel = async () => {
  // 清除场景中的场
  clearField();

  // 清除网格线框
  deleteWireframe();

  // 清除流线
  clearStreamline();

  // 清除网格(Element版)
  clearMesh();

  // 清除网格
  const meshesId = documentManager.getMeshesId();
  Picker().deleteModels(meshesId);

  // 清除拾取空间的模型
  Picker().clickNull();
};

/**
 * 使用Render3D渲染模型,可以设置是否显示wireframe,是否可拾取等
 * @param data 需要渲染的数据
 * @param visibleWireframe 是否显示wireframe
 * @param pickable 是否可拾取
 * @param isMesh 是否是网格
 * @returns 是否渲染成功
 */
const renderModels = async (
  data: IData[],
  visibleWireframe: boolean = false,
  pickable = true,
) => {
  if (!isArray(data)) {
    return false;
  }

  const ids = data.map((item) => item.id);
  // 创建objectInfo，并添加到dataManager中，Renderer的saveShapeDatas会依赖此对象
  data.forEach((object: IData) => {
    dataManager.addObjectsInfo(createModelInfo(object.id, true));
  });
  // 因为 直接使用 renderModels 可能会出现模型无法渲染的问题,
  // 所以 先保存shape数据，再通过drawNewModels传入id数组，渲染对应的模型
  Renderer().saveShapeDatas(data);
  await Renderer().drawNewModels(
    ids,
    pickable ? GirdType.allHas : GirdType.density,
  );
  await renderInsertPoint(data);
  ids.length = 0;
  dataManager.objectsInfoMap.forEach((_, id) => {
    ids.push(id);
  });

  renderWireFrame(data, visibleWireframe);

  refreshCanvas();
  return true;
};

export const renderInsertPoint = async (data: IData[]) => {
  data.forEach((object: IShapeData) => {
    const vertices = [[], [], []]; // [0: 空间点, 1: 硬点, 2: 焊接点]
    const renderType = (object.vertices as any)?.renderType;
    if (renderType?.length > 0) {
      object.vertices?.topo.forEach((item, index) => {
        vertices[renderType[index]].push(...item);
      });
      creatPoints(vertices, object.id);
    }
  });
};

const creatPoints = (vertices: Array<Array<number[]>>, id: string) => {
  vertices?.forEach((item: Array<number[]>, index: number) => {
    if (item?.length > 0) {
      const renderPoint = new CSPoint({
        pointType: PointType[index],
        transparent: false,
        depthTest: true,
        size: 6,
        layers: 0,
        modelDxid: id,
      });
      renderPoint.setVertexs(item);
      renderPoint.addTo(ctx2.cadScene.pointGroup);
    }
  });
};

/**
 * 创建材质
 * @param color 材质颜色
 */
const createMaterial = function (
  color?: Color | string | number,
  wireframe?: boolean,
  clippingPlanes?: Plane[],
) {
  const meshMaterial = new MeshPhongMaterial({
    polygonOffset: true,
    polygonOffsetFactor: -2,
    polygonOffsetUnits: -2,
    color: color,
    // roughness: 0.4, // standard
    shininess: 100,
    side: DoubleSide,
    transparent: true,
    opacity: 1,
    wireframe: wireframe,
    clippingPlanes: clippingPlanes,
  });
  return meshMaterial;
};

/**
 * 删除线框,如果没有传dxids,则删除全部
 * @param dxids 模型id数组
 */
const deleteWireframe = (dxids?: string[]) => {
  let objs = dxids;
  if (!objs) {
    objs = [
      ...documentManager.getMeshesId(),
      ...documentManager.getGeometriesId(),
    ];
  }
  for (const key of objs) {
    // 删除wireframe
    const wireframeDxid = makeWireframeDxid(key);
    const wireframeModel = findModelInGroup(
      ctx2.cadScene.meshGroup,
      wireframeDxid,
    );
    wireframeModel?.destroy();
  }
  refreshCanvas();
};

const renderWireFrame = (data: IData[], visibleWireframe: boolean) => {
  const ids = data.map((item) => item.id);
  for (const key of ids) {
    const wireframeDxid = makeWireframeDxid(key);
    const originModel = Picker().getModelMesh(key);
    if (!originModel) {
      continue;
    }
    const meshGeo = originModel.Geometry;
    if (!meshGeo) {
      continue;
    }

    const shapeData = data.find((item) => item.id === key);
    if (!isShapeData(shapeData) || !isArray(shapeData?.faces)) {
      continue;
    }

    const wires = makeWires(shapeData.faces);

    const group = ctx2.cadScene.meshGroup;
    if (wires?.length > 0) {
      const line = makeWireframeLine(
        wireframeDxid,
        originModel.geometry.attributes.position,
        wires,
      );
      line.visible = visibleWireframe;
      // 销毁旧的
      findModelInGroup(group, line.modelDxid)?.destroy();
      line.addTo(group);
    } else {
      // 没有wires,则直接使用model的geometry来显示wireframe
      const wireframeModel = originModel.cloneSelf();
      wireframeModel.modelDxid = key;
      // 销毁旧的
      findModelInGroup(group, key)?.destroy();
      wireframeModel.material = createMaterial(0x000000, true);
      wireframeModel.setRenderOrder(RENDERORDER.MODEL_BRUSHED);
      wireframeModel.visible = visibleWireframe;
      wireframeModel.addTo(group);
    }
  }

  refreshCanvas();
};

/**
 * 清除场景中的 场模型
 */
const clearField = () => {
  const objs = dataManager.getObjectsInfoMap();
  const keys = Array.from(objs.keys()).map(makeFieldDxid); // 与renderFields保持一致
  Picker().deleteModels(keys);
};

/**
 * 渲染场模型
 */
const renderFields = (fields: IFieldData[], colorMap: string = 'jet') => {
  const colormap = makeColorMap(colorMap);
  const colormapLength = colormap.length;
  const itemSize = colormap[0].length;
  for (let i = 0; i < fields.length; i++) {
    const item = fields[i];
    // 生成顶点颜色数据
    const vColors = item.field.flatMap((value: number) => {
      let index = Math.round(value * colormapLength);
      index = Math.max(0, Math.min(index, colormapLength - 1));
      return colormap[index];
    });
    const model = Picker().getModelMesh(item.id);
    if (model) {
      const colors = new BufferAttribute(new Float32Array(vColors), itemSize);
      // 将场数据对应颜色更新到网格
      model.geometry.attributes.color = colors;
      model.geometry.attributes.color.needsUpdate = true;
      model.material.vertexColors = true;
      model.material.needsUpdate = true;
      // 将场数据对应颜色更新到网格线框
      const wireframeModel = findModelInGroup(
        ctx2.cadScene.meshGroup,
        makeWireframeDxid(item.id),
      );
      if (wireframeModel?.geometry && wireframeModel.material) {
        // LineBasicMaterial 的行为和 MeshXXXMaterial 的不一样,无法通过修改 vertexColors=true 直接更新
        wireframeModel.geometry.attributes.color = colors;
        wireframeModel.material = new LineBasicMaterial({ vertexColors: true });
      }
    }
  }
  refreshCanvas();
};

const resetMeshAndWireframe = (ids: string[]) => {
  ids.forEach((id) => {
    const model = Picker().getModelMesh(id);
    if (model) {
      // 恢复网格面颜色
      model.material.vertexColors = false;
      model.material.color = new Color(ColorSchema.view.shell);
      model.material.needsUpdate = true;
      // 恢复网格线框颜色
      const wireframeModel = findModelInGroup(
        ctx2.cadScene.meshGroup,
        makeWireframeDxid(id),
      );
      if (wireframeModel?.geometry && wireframeModel.material) {
        wireframeModel.material = GlobalLineMaterial;
      }
    }
  });

  refreshCanvas();
};

const streamlineIds: string[] = [];
const clearStreamline = () => {
  for (const id of streamlineIds) {
    const model = Picker().getModelMesh(id);
    if (!model) {
      continue;
    }
    model.destroy();
  }
  streamlineIds.length = 0;

  refreshCanvas();
};

const renderStreamline = (data: IStreamlineData[]) => {
  const colormap = makeColorMap('jet');
  clearStreamline();
  data.forEach((object: IStreamlineData) => {
    const mesh = makeStreamlineMesh(object, colormap);
    mesh.addTo(ctx2.cadScene.meshGroup);
    streamlineIds.push(object.id);
  });

  refreshCanvas();
};

const meshIds: string[] = [];
const clearMesh = () => {
  for (const modelDxid of meshIds) {
    // 从场景中删除
    Picker().getModelMesh(modelDxid)?.destroy();
    Picker().getModelEdge(modelDxid)?.destroy();
    Picker().getModelVertex(modelDxid)?.destroy();
    Picker().getPickSecenMesh(modelDxid)?.destroy();
    Picker().getPickSecenEdge(modelDxid)?.destroy();
    Picker().getPickSecenVertex(modelDxid)?.destroy();
  }
  refreshCanvas();
  meshIds.length = 0;
};

const renderMesh = (data: IMeshData[]) => {
  clearMesh();
  const scene = ctx2.cadScene;
  const pickingScene = ctx2.viewer.sceneSetup.pickScene;
  data.forEach((object: IMeshData) => {
    const { d0PickingMesh, d1Mesh, d1PickingMesh, d2d3Mesh, d2d3PickingMesh } =
      makeMesh(object);
    d0PickingMesh?.addTo(ctx2.viewer.sceneSetup.pickScene.pointGroup);
    d1Mesh?.addTo(scene.edgeGroup);
    d1PickingMesh?.addTo(pickingScene.edgeGroup);
    d2d3Mesh?.addTo(scene.meshGroup);
    d2d3PickingMesh?.addTo(pickingScene.meshGroup);
    meshIds.push(object.id);
  });
  refreshCanvas();
};

const renderCurves = (data: IData[]) => {
  if (data?.length > 0) {
    const streamlineData: IStreamlineData[] = data.filter(isStreamlineData);
    renderStreamline(streamlineData);
  }
};

const renderPoints = (points: Vector3Tuple[]) => {
  clearPoints();
  if (!points || points.length === 0) {
    return;
  }
  const toolsGroup = ctx2.cadScene.toolsGroup;

  const pointsMesh = makeTexturePoints(points);
  pointsMesh.renderOrder = 999;
  toolsGroup.add(pointsMesh);
  refreshCanvas();
};

const clearPoints = () => {
  const toolsGroup = ctx2.cadScene.toolsGroup;
  toolsGroup.clear();
  refreshCanvas();
};

/**
 * 从渲染场景中删除指定id的模型(几何/网格)
 * @param modelDxids
 */
const deleteModels = (modelDxids: string[]) => {
  Picker().deleteModels(modelDxids);
  deleteWireframe(modelDxids);
  refreshCanvas();
};

export {
  clearField,
  clearMesh,
  clearModel,
  clearPoints,
  clearStreamline,
  deleteModels,
  renderCurves,
  renderFields,
  renderMesh,
  renderModels,
  renderPoints,
  resetMeshAndWireframe,
};
